import pygame
import socket
import sys
import os
import math
import threading
from pygame.locals import *
from server import Server
from client import Client
from unit import UnitDB, Unit
from threading import Thread
from combat import *

def load_png(name):
    """Loads image from data/
    
    Returns Surface and Rect
    """
    fullname = os.path.join('data', name)
    try:
        image = pygame.image.load(fullname)
        if image.get_alpha() is None:
            image = image.convert()
        else:
            image = image.convert_alpha()
    except pygame.error, message:
        print 'Cannot load image:', fullname
        raise SystemExit, message
    return image, image.get_rect()

class UIUnitDB(UnitDB):
    """UnitDB for UIClient
    UIUnitDB inherets UnitDB and makes changes necessary for use with UIClient.
    """
    def _add_unit(self, msg):
        """Adds the specified UIUnit to the database."""
        self.units.append(UIUnit(msg[3], msg[4]))

class UIUnit(pygame.sprite.Sprite, Unit):
    """Unit for UIClient

    UIUnit inherits Unit and makes changes necessary for use with UIClient.
    
    Calls to graphics_tick advance units smoothly to the next communications
    state.
    """
    def __init__(self, type, pos):
        pygame.sprite.Sprite.__init__(self)
        Unit.__init__(self, type, pos)
        self.image, self.rect = load_png('image.png')
        self.sprite_pos = self.old_pos = self.pos = pos
        self.selected = 0

    def move(self):
        """Moves and updates sprite location."""
        self.sprite_pos = self.old_pos = self.pos
        Unit.move(self)

    def graphics_tick(self, screen, tdelta):
        """Moves and update sprite between client ticks."""
        if self.rect.center != self.pos:
            self.rect.center = self._graphics_move(tdelta)
        screen.blit(self.image, self.rect)

    def _graphics_move(self, tdelta):
        """Calculates position change between client ticks."""
        calc = lambda i: self.sprite_pos[i] + (self.pos[i] -
                self.old_pos[i])*(tdelta/250.0)
        self.sprite_pos = calc(0), calc(1)
        return self.sprite_pos

class UIClient(Client, Thread):
    def __init__(self, hostname, port, resx, resy, fps):
        Client.__init__(self, hostname, port)
        threading.Thread.__init__(self)
        self.resx = resx
        self.resy = resy
        self.unit_db = UIUnitDB()
        self.weapon_db = UIWeaponDB(self.unit_db)
        self.timer = pygame.time.Clock()
        self.sel_start = (0, 0)
        self.fps = fps

    def run(self):
        pygame.init()
        self.timer = pygame.time.Clock()
        self.screen = pygame.display.set_mode((self.resx, self.resy))
        pygame.display.set_caption('Untitled DC-RTS')
        self.selected, self.selected_rect = load_png('selected.png')
        while True:
            for event in pygame.event.get():
                self.handle_event(event)
            self.draw(self.timer.tick(self.fps))

    def unit_blit(self, screen, tdelta):
        """
        Blits all units to Screen
        """
        for unit in self.unit_db.units:
            unit.graphics_tick(screen, tdelta)
            if unit.selected:
                self.selected_rect.center = unit.rect.center
                screen.blit(self.selected, self.selected_rect)

    def weapon_blit(self, screen, tdelta):
        """
        Blits all weapons to Screen
        """
        for weapon in self.weapon_db.units:
            weapon.graphics_tick(screen, tdelta)

    def get_selected(self):
        selected = []
        for unit in self.unit_db.units:
            if unit.selected:
                selected.append(self.unit_db.units.index(unit))
        return selected

    def handle_event(self, event):
        if event.type == QUIT:
            pygame.quit()
            exit()
        elif event.type == MOUSEBUTTONDOWN:
            z = event.dict['pos']
            button = event.dict['button']
            if button == 2:
                self.request_add(1, z)
            if button == 3:
                for unit in range(0,len(self.unit_db.units)-1):
                    if self.unit_db.units[unit].rect.collidepoint(z) and (unit
                            not in self.get_selected()):
                        print "in here"
                        for unid in self.get_selected():
                            print "requesting attack",unid,unit
                            self.request_attack(unid, unit)
                        return
                for unid in self.get_selected():
                    self.request_move(unid, z)
            if button == 1:
                if self.sel_start == (0, 0):
                    self.sel_start = z
        elif event.type == MOUSEBUTTONUP:
            z = event.dict['pos']
            button = event.dict['button']
            if button == 1:
                if self.sel_start != (0, 0):
                    self.select_units(self.sel_start, z)
                    self.sel_start = (0, 0)

    def select_units(self, first, second):
        if first[0] < second[0]:
            if first[1] < second[1]:
                left, top = first
            else:
                left = first[0]
                top = second[1]
        else:
            if first[1] < second [1]:
                left = second[0]
                top = first[1]
            else:
                left, top = second
        mouse_rect = pygame.Rect(left, top, math.fabs(first[0] - second[0]),
                math.fabs(first[1] - second[1]))
        for unit in self.unit_db.units:
            if unit.rect.colliderect(mouse_rect):
                unit.selected = True
            else:
                unit.selected = False

    def draw(self, tdelta):
        self.screen.fill((255, 255, 255))
        self.unit_blit(self.screen, tdelta)
        self.weapon_blit(self.screen, tdelta)
        if self.sel_start != (0,0):
            pos = pygame.mouse.get_pos()
            pygame.draw.rect(self.screen, (0, 255, 0),
                pygame.Rect(self.sel_start[0], self.sel_start[1], pos[0] -
                self.sel_start[0],
                pos[1] - self.sel_start[1]), 1)
        pygame.display.flip()

class Game:
    def __init__(self, hostname, port, is_host, is_client, resx=640, resy=480,
            fps=60, combat_multiply=3, comm_tick=4):
        self.time = 0
        self.is_host = is_host
        self.is_client = is_client
        self.combat_multiply = combat_multiply
        self.comm_tick = comm_tick
        self.count = 0
        if is_host:
            print "Initializing server..."
            self.server = Server(hostname, port)
        if is_client:
            print "Initializing client..."
            self.client = UIClient(hostname, port, resx, resy, fps)
            self.client.start()
        else:
            pygame.init()
            pygame.display.set_mode((resx, resy))
        self.timer = pygame.time.Clock()

    def main(self):
        while True:
            self.count += 1
            tdelta = self.timer.tick(self.comm_tick*self.combat_multiply)
            if self.is_host:
                self.server.combat_tick()
                if (self.count % self.combat_multiply) == 0:
                    self.server.tick()
            if self.is_client:
                self.client.combat_tick()
                if (self.count % self.combat_multiply) == 0:
                    self.client.tick()
            if self.is_client and not self.client.is_alive():
                exit()
